#include "SystemCall.h"

using namespace std;

SystemCall::SystemCall(LowLevelDisk* ll)
{
	_fileSystem = new FileSystem(ll);
	_descTable = new OpenFileDescTable();
}

SystemCall::~SystemCall(){}


bool SystemCall::isDir(char * address) // - Ret urns whether this address is a directory or not. Needs to deal
			//with soft-links.
{
	cout<<"Is dir?: "<<address<<endl;
	int inode = GetInodeFromPath(address,false);
	if (inode == -1){
		cerr<<"trying to approach non-existing file: "<<address<<endl;
		return -1;
	}
	return (_fileSystem->getFileType(inode) == 1); //1=dir

}


int SystemCall::moveFile(char* oldNamePath, char * new_dest)
{
	//remove entry from old location
	int oldFather = GetFatherInode(oldNamePath,false);
	if (oldFather==-1)
	{
		cerr<<"trying to move non-existing file: "<<oldNamePath<<endl;
		return -1;
	}
	string oldName = GetNameFromPath(oldNamePath);
	LibEntry entry = RmvFromDirByString(oldFather,oldName);


//	enter in new location
	int newFather = GetFatherInode(new_dest,true);
	string newName = GetNameFromPath(new_dest);
	AddToDir(newFather,newName,entry.inodeNumber);

	return 0;

}

int SystemCall::Write (int fd, int nBytes,char * Buffer) // - writes nBytes from buffer into the file.
{
	int inode = _descTable->getInodeByFd(fd);
	int offset = _descTable->getOffsetByFd(fd);
	_fileSystem->f_write(inode,Buffer,offset,nBytes);
	return 0;
}


int SystemCall::Read (int fd, int nBytes, char *Buffer) // - reads nBytes from the received file into the buffer.
{
	int inode = _descTable->getInodeByFd(fd);
	_fileSystem->f_read(inode,Buffer,0,nBytes);

	//TODO: what value return? bytes read? need to change f_read as well
	return 0;
}


int SystemCall::Seek(int fd, int location) // - moves the file curser to ‘location’.
{
	_descTable->moveFd(fd,location);
}

int SystemCall::Close(int fd) // - closes the entry of the file in the file table, and frees the entry in the fd table.
{
	_descTable->RemoveFile(fd);
}

int SystemCall::Open(char* filename, int /*flag*/ access_permissions) // - opens the file ‘file-name’. Needs to deal with soft-links. Opens the file with the specified permissions.
{
	cout<<"opening file"<<filename<<endl;

	int father = GetFatherInode(filename,false);
	vector<LibEntry> v = _fileSystem->d_read(father);
	string s = GetNameFromPath(filename);
	int inode = GetInodeByName(v,s);


	int fd = _descTable->AddFile(inode,access_permissions);
 // TODO: handle softt-links
	return fd;
}


int SystemCall::ls(char*dir_name, char * buf) // - lists the content of the location-directory. Return list in buf.
{
	cout<<" ls: "<<dir_name<<endl;
	string s;
	int inode = GetInodeFromPath(dir_name,false);
	vector<LibEntry> v = _fileSystem->d_read(inode);
	for(int i =0; i < v.size(); i++)
	{
		s+= v[i].fileName;
		s+= "\n";
	}
	strcpy(buf,s.c_str());
	cout<<"ls returning: "<<buf<<endl;
	return 0; //success ?

}


int SystemCall::RmFile(char* file_name) // - removes the file ‘file-name’
{
	cout<<"removing file: "<<file_name<<endl;

	int father = GetFatherInode(file_name,false);
	if(father == -1){
		cerr<<"trying to delete a non existing file"<<endl;
		return -1;
	}

	int inode = GetInodeFromPath(file_name, false);
	RmvFromDirByInode(father,inode); //removing entry from directory
	_fileSystem->f_delete(inode);	// freeing inode
	return 0;
}

int SystemCall::RmDir(char* dir_name) // - removes the directory ‘dir-name’
{
	cout<<"removing dir "<<dir_name<<endl;
	int father = GetFatherInode(dir_name,false);
	if(father == -1){
		cerr<<"trying to delete a non existing dir"<<endl;
		return -1;
	}
	vector<LibEntry> v;		//clearing all data from dir, to allow deletion
	_fileSystem->d_write(father, v);
	_fileSystem->d_delete(father);
	return 0;
}



int SystemCall::MakeHLink(char* target_file_name, char*file_name)
{
	// get original file, add hLink count
	int org = GetInodeFromPath(file_name,false);
	string name = GetNameFromPath(file_name);
	 _fileSystem->IncHLinkNum(org);

	int newFather = GetFatherInode(target_file_name,true);
	if (newFather == -1)
		{
		cerr<<"trying to make HL to a non existing file"<<endl;
		return -1;
		}
	vector<LibEntry> v = _fileSystem->d_read(newFather);
	AddToDir(newFather,name,org);
	return 0;
}

// returns of lib | file,
// or -1. if Inode doesnt exist
int SystemCall::GetInodeFromPath(char* path, bool create)
{
	int father = GetFatherInode(path, create);
	if (father == -1) return -1;
	vector<LibEntry> lib = _fileSystem->d_read(father);
	string name = GetNameFromPath(path);
	int ans = GetInodeByName(lib,name);
	return ans;
}
// - creates and opens a new file named
//file_name returning a fd to the new file. If type is soft-link mark it as such.
//type: 0 = file. 2=soft-link
//Opens the file with the specified permissions.
int SystemCall::MakeFile(char* file_name,char type, int /*flag*/ access_permissions)
{
	int inode = _fileSystem->createFile(type);
	cout<<"inode =  "<<inode ;

	int fd = _descTable->AddFile(inode,access_permissions);
	//mark as soft-link if is one

	//getting father directory, adding new file entry
	int fatherInode = GetFatherInode(file_name,true);
	string fileName = GetNameFromPath(file_name);

	AddToDir(fatherInode,fileName,inode);

	return fd;
}


int SystemCall::MakeDir(char* dirPath)
{
	int inode = _fileSystem->createDir();
		cout<<"directory inode =  "<<inode ;
	int fatherInode = GetFatherInode(dirPath,true);
	string dirName = GetNameFromPath(dirPath);

	AddToDir(fatherInode,dirName,inode);

}

//int SystemCall::MakeHLink(char* target_file_name, char*file_name); // - creates a hard link with name file-name,
		//linked to target.
//int SystemCall::RmDir(char* dir_name); // - removes the directory ‘dir-name’
//int SystemCall::RmFile(char* file_name); // - removes the file ‘file-name’
//int SystemCall::ls(char*dir_name, char * buf); // - lists the content of the location-directory. Return list in buf.
//int SystemCall::Open(char* filename, int /*flag*/ access_permissions); // - opens the file ‘file-name’. Needs to deal with soft-links. Opens the file with the specified permissions.
//int SystemCall::Close(int fd); // - closes the entry of the file in the file table, and frees the entry in the fd table.
//int SystemCall::Seek(int fd, int location); // - moves the file curser to ‘location’.
//int SystemCall::Read (int fd, int nBytes, char *Buffer); // - reads nBytes from the received file into the buffer.
//int SystemCall::Write (int fd, int nBytes,char * Buffer); // - writes nBytes from buffer into the file.
//int SystemCall::moveFile(char* parendDir, char * new_dest); // - Moves the child's i-node from its current directory
//		//to new destination.
//bool SystemCall::isDir(char * address); // - Ret urns whether this address is a directory or not. Needs to deal
		//with soft-links.


//returns file name from full path
string SystemCall::GetNameFromPath(char* path)
{
	vector<string> dirs = split(path,'/');
	cout<<"file name is "<< dirs[dirs.size()-1]<<endl;
	return dirs[dirs.size()-1];
}



// gets full path of file, returns Inode of fatherfd
// if doesnt exist: creates by bool var
int SystemCall::GetFatherInode(char* path, bool create)
{
	cout<<"GetFatherInode from:  "<< path<<endl;
	int currInode = 0;
	int nextInode = 0;
	vector<LibEntry> libVec;
	vector<string> dirs = split(path,'/'); //all directories, [0]=root
	int fatherIdx = dirs.size()-2;
	if (fatherIdx == 0){//file in root
		return 0;
	}
	else
	{
		string fatherName = dirs[fatherIdx]; //::  /father/filename

		for (int i = 0; i < dirs.size()-1; ++i)  //get all inodes by order

		{
			//checking if inode exists
			if (nextInode == -1) {//not a directory
				if (create)
				{
					cout<<"dir <"<<dirs[i]<<"> doesnt exist, creating"<<endl;
					nextInode = _fileSystem->createDir();
					AddToDir(currInode,dirs[i],nextInode);

				}
				else {return -1;} //directory doenst exist, not creating itfd
			}
			currInode = nextInode;
			libVec = _fileSystem->d_read(nextInode); //inode  -> read directory
			nextInode = GetInodeByName(libVec,dirs[i+1]); // name = next lib - returns -1 if doesnt exist
		}
		cout<<"GetFatherInode ="<< currInode<<endl;
		return currInode; //TODO:: check that returns correct inode
	}

}

// removes inode entry from dir, returns libEntry
LibEntry SystemCall::RmvFromDirByString(int inodeFather, string name)
{
	LibEntry lib;
	vector<LibEntry> v = _fileSystem->d_read(inodeFather);
	vector<LibEntry> newVec;
	for (int i = 0; i < v.size(); ++i) {
		if (v[i].fileName != name)
			newVec.push_back(v[i]);
		else
			lib = v[i];
	}

	_fileSystem->d_write(inodeFather,newVec);
	 return lib;
}



// removes inode entry from dir, returns libEntry
LibEntry SystemCall::RmvFromDirByInode(int inodeFather, int inode)
{
	LibEntry lib;
	vector<LibEntry> v = _fileSystem->d_read(inodeFather);
	vector<LibEntry> newVec;
	for (int i = 0; i < v.size(); ++i) {
		if (v[i].inodeNumber != inode)
			newVec.push_back(v[i]);
		else
			lib = v[i];
	}

	_fileSystem->d_write(inodeFather,newVec);
	 return lib;
}

// adds tp directory inodeFather, a new entry <name, newInode>
void SystemCall::AddToDir(int inodeFather,string name, int newInode)
{
	LibEntry lib;
	lib.fileName = name;
	lib.inodeNumber = newInode;
	vector<LibEntry> v = _fileSystem->d_read(inodeFather);
	v.push_back(lib);
	_fileSystem->d_write(inodeFather,v);
}

//returns -1 if inode doesnt exist
int SystemCall::GetInodeByName(vector<LibEntry> vec, string name)
{
	vector<LibEntry>:: iterator it;
//	for(it = vec.begin(); it != vec.end(); it++)
	for (int i = 0; i < vec.size(); i++) {
//		if (*it->fileName == name)
		if (vec[i].fileName == name)
			return vec[i].inodeNumber;
			//			return (*it).inodeNumber;
	}
	return -1;
}

// se[erated string by delimiter
vector<string> SystemCall::split (string s,char c)
{
	vector<string> res;
	string tmp = "";
	for (int i = 0; i <= s.size(); ++i) {
		if(i!=s.size() && s.at(i)!= c){
			tmp.push_back(s.at(i));
		}
		else
		{
			res.push_back(tmp);
			tmp = "";
		}
	}
	return res;
}
